package com.xy.blog.article.domain.service.impl;


import com.alibaba.fastjson.JSON;
import com.xy.blog.article.api.model.ArticleQuery;
import com.xy.blog.article.domain.constants.ArticleConstantsType;
import com.xy.blog.article.domain.dao.*;
import com.xy.blog.article.domain.dao.impl.ArticleDaoImpl;
import com.xy.blog.article.domain.dao.model.*;
import com.xy.blog.article.domain.exception.ArticleException;
import com.xy.blog.article.domain.exception.constants.ArticleCategoryErrorCode;
import com.xy.blog.article.domain.exception.constants.ArticleErrorCode;
import com.xy.blog.article.domain.mapstruct.ArticleConverter;
import com.xy.blog.article.domain.service.IAnchorService;
import com.xy.blog.article.domain.service.IArticleService;
import com.xy.blog.article.domain.service.model.AnchorBO;
import com.xy.blog.article.domain.service.model.ArticleBO;
import com.xy.blog.article.domain.service.oss.OSSServices;
import com.xy.blog.article.domain.utils.MarkdownUtils;
import com.xy.blog.core.id.SnowflakeIdWorker;
import com.xy.blog.core.springmvc.result.page.PageReq;
import com.xy.blog.core.springmvc.result.page.PageResp;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author zanhonglei
 * @version V1.0
 * @Description:
 * @date 2019/10/30 23:13
 */
@Service
public class ArticleServiceImpl implements IArticleService {

    private static Logger logger = LoggerFactory.getLogger(ArticleServiceImpl.class);

    @Autowired
    private ArticleConverter articleConverter;

    @Autowired
    private ArticleDaoImpl articleDaoImpl;

    @Autowired
    private IArticleTagDao articleTagDaoImpl;

    @Autowired
    private IArticleCategoryDao articleCategoryDaoImpl;

    @Autowired
    private IArticleCategoryDao iArticleCategoryDaoImpl;

    @Autowired
    private IAnchorService htmlAnchorServiceImpl;

    @Autowired
    private IAnchorService markDownAnchorServiceImpl;

    @Autowired
    private IArticlePicTempDao articlePicTempDaoImpl;

    @Autowired
    private IArticlePicDao articlePicDaoImpl;

    @Autowired
    private OSSServices ossServices;

    @Autowired
    private ISpecialColumnDao specialColumnDaoImpl;


    @Override
    public PageResp<ArticleBO> blogHomeArticleList(PageReq<ArticleQuery> req) throws ArticleException {
        PageResp<ArticleDO> articleDOPageResp = articleDaoImpl.blogHomeList(req);
        PageResp<ArticleBO> articleBOPageResp = articleConverter.do2Bo(articleDOPageResp);
        List<ArticleBO> list = articleBOPageResp.getList();
        List<Long> articleIds = new ArrayList<>();
        list.forEach(item ->{
            articleIds.add(item.getId());
        });

        Map<Long, Long> likeCountByArticleId = articleDaoImpl.getLikeCountByArticleId(articleIds);

        Map<Long, Long> viewCountByArticleId = articleDaoImpl.getViewCountByArticleId(articleIds);

        list.forEach(item->{
            Long id = item.getId();
            item.setLikeCount(likeCountByArticleId.get(id));
            item.setViewCount(viewCountByArticleId.get(id));
        });

        return articleBOPageResp;
    }

    @Override
    public PageResp<ArticleBO> blogHomeHotrticleList(PageReq<ArticleQuery> req) throws ArticleException {
        return null;
    }

    @Override
    public PageResp<ArticleBO> listArticlesByPage(PageReq<ArticleQuery> req) throws ArticleException {
        //        Page<ArticleBO> page = PageHelper.startPage(1, 10).doSelectPage(()-> articleMapper.selectAll());
        //       PageInfo pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(() -> articleMapper.selectAll());
//        Pageable pageable = new QPageRequest(req.getPageNum(),req.getPageSize());
//        articleMapper.
//        Page<ArticleDO> all = articleMapper.findAll(pageable);
//        List<ArticleDO> result = new ArrayList<>();
//        all.get().forEach(result::add);

        ArticleQuery params = req.getParams();
        Set<Long> ids = new HashSet<>();
        if (params != null && params.getCategoryId() != null) {
            Long categoryId = params.getCategoryId();
            ArticleCategoryRelDO articleCategoryRelDO = new ArticleCategoryRelDO();
            articleCategoryRelDO.setCategoryId(categoryId);
            List<ArticleCategoryRelDO> articleCategoryRelDOS = articleCategoryDaoImpl.selectRel(articleCategoryRelDO);
            for (ArticleCategoryRelDO item : articleCategoryRelDOS) {
                ids.add(item.getArticleId());
            }
            if (ids.isEmpty()) {
                ids.add(0L);
            }
        }

        if (params != null && params.getSpecialColumnId() != null) {
            Long specialColumnId = params.getSpecialColumnId();
            ArticleSpecialColumnRelDO articleSpecialColumnRelDO = new ArticleSpecialColumnRelDO();
            articleSpecialColumnRelDO.setSpecialColumnId(specialColumnId);
            List<ArticleSpecialColumnRelDO> articleSpecialColumnRelDOS = specialColumnDaoImpl.selectRel(articleSpecialColumnRelDO);
            for (ArticleSpecialColumnRelDO item : articleSpecialColumnRelDOS) {
                ids.add(item.getArticleId());
            }
            if (ids.isEmpty()) {
                ids.add(0L);
            }
        }


        PageResp<ArticleDO> articleDOPageResp = articleDaoImpl.listPage(req,ids);
        PageResp<ArticleBO> articleBOPageResp = articleConverter.do2Bo(articleDOPageResp);
        List<Long> articleIds = new ArrayList<>();
        articleBOPageResp.getList().forEach(item->{
            ArticleCategoryDO categoryDO = iArticleCategoryDaoImpl.getById(item.getCategoryId());
            item.setCategoryName(categoryDO == null ? "" : categoryDO.getName());
            ArticleTagRelDO articleTagRelDO = new ArticleTagRelDO();
            articleTagRelDO.setArticleId(item.getId());
            List<ArticleTagRelDO> articleTagRelDOS = articleTagDaoImpl.selectRel(articleTagRelDO);
            List<String> tags = new ArrayList<>();
            articleTagRelDOS.forEach(tagRelDO->{
                ArticleTagDO tagDO = articleTagDaoImpl.getById(tagRelDO.getTagId());
                tags.add(tagDO.getName());
            });
            item.setTags(tags);

            articleIds.add(item.getId());
        });

        Map<Long, Long> likeCountByArticleId = articleDaoImpl.getLikeCountByArticleId(articleIds);
        Map<Long, Long> viewCountByArticleId = articleDaoImpl.getViewCountByArticleId(articleIds);

        articleBOPageResp.getList().forEach(item->{
            Long id = item.getId();
            Long likeCount = likeCountByArticleId.get(id);
            Long viewCount = viewCountByArticleId.get(id);
            item.setLikeCount(likeCount);
            item.setViewCount(viewCount);
        });

        return articleBOPageResp;
    }

    @Override
    public ArticleBO getArticleById(Long id) throws ArticleException{
        ArticleDO byId = articleDaoImpl.getById(id);
        ArticleContentDO content = articleDaoImpl.getContentById(id);
        if (content == null) {
            return new ArticleBO();
        }
        ArticleBO articleBO = articleConverter.do2Bo(byId);
        articleBO.setContentHtml(content.getContentHtml());
        articleBO.setContent(content.getContent());
        articleBO.setType(byId.getType());
        List<Long> ids = new ArrayList<>();
        ids.add(id);
        Map<Long, Long> likeCountByArticleId = articleDaoImpl.getLikeCountByArticleId(ids);
        Map<Long, Long> viewCountByArticleId = articleDaoImpl.getViewCountByArticleId(ids);
        articleBO.setViewCount(viewCountByArticleId.get(id));
        articleBO.setLikeCount(likeCountByArticleId.get(id));

        ArticleTagRelDO articleTagRelDO = new ArticleTagRelDO();
        articleTagRelDO.setArticleId(id);
        List<ArticleTagRelDO> articleTagRelDOS = articleTagDaoImpl.selectRel(articleTagRelDO);
        List<String> tags = new ArrayList<>();
        articleTagRelDOS.forEach(tagRelDO->{
            ArticleTagDO tagDO = articleTagDaoImpl.getById(tagRelDO.getTagId());
            tags.add(tagDO.getName());
        });
        articleBO.setTags(tags);
        ArticleCategoryRelDO articleCategoryRelDO = new ArticleCategoryRelDO();
        articleCategoryRelDO.setArticleId(id);
        List<ArticleCategoryRelDO> articleCategoryRelDOS = articleCategoryDaoImpl.selectRel(articleCategoryRelDO);
        if (articleCategoryRelDOS != null && !articleCategoryRelDOS.isEmpty()) {
            ArticleCategoryRelDO categoryRelDO = articleCategoryRelDOS.get(0);
            articleBO.setCategoryId(categoryRelDO.getCategoryId());
            ArticleCategoryDO categoryDO = articleCategoryDaoImpl.getById(categoryRelDO.getCategoryId());
            articleBO.setCategoryName(categoryDO.getName());
        }
        return articleBO;
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void updateArticle(ArticleBO articleBO) throws ArticleException {
        Long id = articleBO.getId();
        ArticleDO isExits = articleDaoImpl.getById(id);
        if (isExits == null) {
            throw new ArticleException(ArticleErrorCode.ARTICLE_NOT_EXITS);
        }
        LocalDateTime now = LocalDateTime.now();
        String content = articleBO.getContent();
        LocalDateTime createTime = articleBO.getCreateTime();
        Long categoryId = articleBO.getCategoryId();
        ArticleDO needUpdateDO = articleConverter.bo2Do(articleBO);
        String html = MarkdownUtils.markdownToHtml(content);
        //生成锚点
        List<AnchorBO> anchorBOS = null;
        if (articleBO.getType().equals(ArticleConstantsType.TEXT)) {
            anchorBOS = htmlAnchorServiceImpl.generateAnchor(content);
        } else {
            anchorBOS = markDownAnchorServiceImpl.generateAnchor(content);
        }

        needUpdateDO.setAnchor(JSON.toJSONString(anchorBOS));
        needUpdateDO.setUpdateTime(now);
        articleDaoImpl.update(needUpdateDO);


        ArticleContentDO articleContentDO = new ArticleContentDO();
        articleContentDO.setArticleId(id);
        articleContentDO.setContentHtml(html);
        articleContentDO.setContent(content);
        articleContentDO.setUpdateTime(now);
        articleDaoImpl.updateContent(articleContentDO);


        articleTagDaoImpl.deleteRelByArticleId(id);
        List<String> tags = articleBO.getTags();
        tags.forEach(name->{
            Long tagId ;
            ArticleTagDO articleTagDO = new ArticleTagDO();
            articleTagDO.setName(name);
            List<ArticleTagDO> select = articleTagDaoImpl.select(articleTagDO);
            if (!select.isEmpty()) {
                ArticleTagDO temp = select.get(0);
                tagId = temp.getId();
            } else {
                tagId = SnowflakeIdWorker.generateId();
                articleTagDO.setId(tagId);
                articleTagDaoImpl.save(articleTagDO);
            }

            ArticleTagRelDO articleTagRelDO = new ArticleTagRelDO();
            articleTagRelDO.setArticleId(id);
            articleTagRelDO.setTagId(tagId);
            articleTagRelDO.setCreateTime(createTime);
            articleTagDaoImpl.saveRel(articleTagRelDO);
        });

        articleCategoryDaoImpl.deleteRelByArticleId(id);
        ArticleCategoryDO articleCategoryDO = new ArticleCategoryDO();
        articleCategoryDO.setId(categoryId);
        int i = articleCategoryDaoImpl.selectCount(articleCategoryDO);
        if (i == 0) {
            throw new ArticleException(ArticleCategoryErrorCode.NOT_EXISTS);
        }

        ArticleCategoryRelDO articleCategoryRelDO = new ArticleCategoryRelDO();
        articleCategoryRelDO.setCategoryId(categoryId);
        articleCategoryRelDO.setCreateTime(createTime);
        articleCategoryRelDO.setArticleId(id);
        articleCategoryDaoImpl.saveRel(articleCategoryRelDO);

    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void add(ArticleBO articleBO) throws ArticleException {

        ArticleDO articleDO = new ArticleDO();
        Long articleId = SnowflakeIdWorker.generateId();
        articleDO.setArticleType(articleBO.getArticleType());
        articleDO.setPublishType(articleBO.getPublishType());
        articleDO.setCourse(articleBO.getCourse());
        articleDO.setTitle(articleBO.getTitle());
        Integer type = articleBO.getType();
        articleDO.setType(type);
        String content = articleBO.getContent();
        if (content.length() < 30) {
            articleDO.setDescription(content);
        } else {
            articleDO.setDescription(content.substring(0,30));
        }
        articleDO.setId(articleId);
        String html = MarkdownUtils.markdownToHtml(content);
        List<AnchorBO> anchorBOS = null;
        if (type.equals(ArticleConstantsType.MARKDOWN)) {
            //生成锚点
            anchorBOS = markDownAnchorServiceImpl.generateAnchor(content);
        } else {
            anchorBOS = htmlAnchorServiceImpl.generateAnchor(content);
        }
        articleDO.setAnchor(JSON.toJSONString(anchorBOS));

        String pattern="!.*\\(.*https://.*\\)";
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(content);
        if (m.find()) {
            String mdImage = m.group(0);
            String homeImage = mdImage.substring(mdImage.indexOf("(") + 1, mdImage.lastIndexOf(")"));
            articleDO.setHomeImage(homeImage);
        } else {
            articleDO.setHomeImage(null);
        }


        ArticleContentDO articleContentDO = new ArticleContentDO();
        articleContentDO.setArticleId(articleId);
        articleContentDO.setContentHtml(html);
        articleContentDO.setContent(content);
        articleDaoImpl.addContent(articleContentDO);

        Long categoryId = articleBO.getCategoryId();

        List<String> tags = articleBO.getTags();
        tags.forEach(name->{
            Long tagId ;
            ArticleTagDO articleTagDO = new ArticleTagDO();
            articleTagDO.setName(name);
            List<ArticleTagDO> select = articleTagDaoImpl.select(articleTagDO);
            if (!select.isEmpty()) {
                ArticleTagDO temp = select.get(0);
                tagId = temp.getId();
            } else {
                tagId = SnowflakeIdWorker.generateId();
                articleTagDO.setId(tagId);
                articleTagDaoImpl.save(articleTagDO);
            }

            ArticleTagRelDO articleTagRelDO = new ArticleTagRelDO();
            articleTagRelDO.setArticleId(articleId);
            articleTagRelDO.setTagId(tagId);
            articleTagDaoImpl.saveRel(articleTagRelDO);
        });


        Integer course = articleBO.getCourse();
        Integer maxOrder = null;
        if (ArticleConstantsType.COURSE.equals(course)) {
            Long specialColumnId = articleBO.getSpecialColumnId();
            //教程
            ArticleSpecialColumnRelDO articleSpecialColumnRelDO = new ArticleSpecialColumnRelDO();
            articleSpecialColumnRelDO.setArticleId(articleId);
            articleSpecialColumnRelDO.setSpecialColumnId(specialColumnId);
            specialColumnDaoImpl.saveRel(articleSpecialColumnRelDO);

            maxOrder = articleDaoImpl.getMaxOrderBySpecialColumn(specialColumnId);
        } else if (ArticleConstantsType.ARTICLE.equals(course)) {
            //文章
            ArticleCategoryDO articleCategoryDO = new ArticleCategoryDO();
            articleCategoryDO.setId(categoryId);
            int i = articleCategoryDaoImpl.selectCount(articleCategoryDO);
            if (i == 0) {
                throw new ArticleException(ArticleCategoryErrorCode.NOT_EXISTS);
            }
            ArticleCategoryRelDO articleCategoryRelDO = new ArticleCategoryRelDO();
            articleCategoryRelDO.setCategoryId(categoryId);
            articleCategoryRelDO.setArticleId(articleId);
            articleCategoryDaoImpl.saveRel(articleCategoryRelDO);

            maxOrder = articleDaoImpl.getMaxOrderByCategoryId(categoryId);
        }
        articleDO.setOrders(maxOrder == null ? 0 : maxOrder + 1);




        articleDaoImpl.add(articleDO);




        //async todo
        ArticleLikeDO like = new ArticleLikeDO();
        like.setCount(1L);
        like.setArticleId(articleId);
        articleDaoImpl.addLikeCount(like);
        ArticleViewDO view = new ArticleViewDO();
        view.setCount(1L);
        view.setArticleId(articleId);
        articleDaoImpl.addViewCount(view);

    }

    @Override
    @Async
    @Transactional(rollbackFor = Throwable.class)
    public void deleteArticleById(Long id) throws ArticleException{
        articleDaoImpl.delete(id);
        articleDaoImpl.deleteContentByArticleId(id);
        articleCategoryDaoImpl.deleteRelByArticleId(id);
        articleDaoImpl.deleteLikeByArticleId(id);
        articleDaoImpl.deleteViewByArticleId(id);
        articleTagDaoImpl.deleteRelByArticleId(id);

        // 放入到回收站

    }

    @Override
    public String upload(MultipartFile file) throws ArticleException{
        Long id = SnowflakeIdWorker.generateId();
        String localPath = null;
        try {
            byte[] bytes = file.getBytes();
            StringBuffer filePathTemp = new StringBuffer();
            filePathTemp.append(LocalDate.now().toString()).append("/").append(LocalTime.now().format(DateTimeFormatter.ofPattern("HHmmss"))).append("/").append(id);
            localPath = filePathTemp.toString();
            ossServices.saveFile(bytes,localPath);
            ArticlePicTempDO articlePicTempDO = new ArticlePicTempDO();
            articlePicTempDO.setId(id);
            articlePicTempDO.setLocalPath(localPath);
            articlePicTempDaoImpl.save(articlePicTempDO);
        } catch (Exception e) {
            logger.error(ExceptionUtils.getStackTrace(e));
            return null;
        }
        return ossServices.getUrl(localPath);
    }


    @Override
    public ResponseEntity<byte[]> download(Long picId) throws ArticleException{

        ArticlePicDO master = articlePicDaoImpl.getById(picId);
        String filePath = null;
        if (master == null) {
            ArticlePicTempDO byId = articlePicTempDaoImpl.getById(picId);
            if (byId == null) {
                return null;
            }
            filePath = byId.getLocalPath();
        } else {
            filePath = master.getLocalPath();
        }
        try {
            //获取文件

            File file = new File(filePath);
            //获取文件名
            //解决文件名乱码
            String fileName =new String(file.getName().getBytes("UTF-8"),"iso-8859-1");

            //读取二进制文件
            byte[] body = null;
            InputStream is = new FileInputStream(file);
            body = new byte[is.available()];
            is.read(body);
            HttpHeaders headers = new HttpHeaders();
            //通知浏览器以attachment（下载方式）打开图片
            headers.add("Content-Disposition", "attchement;filename=" + fileName);

            //application/octet-stream二进制流数据（最常见的文件下载）。
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

            //文件下载的Http协议中的状态最好使用HttpStatus.OK。
            HttpStatus statusCode = HttpStatus.OK;
            return new ResponseEntity<byte[]>(body, headers, statusCode);
        } catch (Exception e) {
            logger.error(ExceptionUtils.getStackTrace(e));
        }
        return null;
    }

    @Override
    public void like(Long id) throws ArticleException {
        ArticleLikeDO articleLikeDO = new ArticleLikeDO();
        articleLikeDO.setArticleId(id);
        articleLikeDO.setCount(1L);
        articleDaoImpl.addLikeCount(articleLikeDO);
    }

    @Override
    public void view(Long id) throws ArticleException {
        ArticleViewDO articleViewDO = new ArticleViewDO();
        articleViewDO.setArticleId(id);
        articleViewDO.setCount(1L);
        articleDaoImpl.addViewCount(articleViewDO);
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void changeOrders(String ordId, String newId) {
        ArticleDO oldObj = new ArticleDO();
        oldObj.setId(Long.parseLong(ordId));
        ArticleDO newObj = new ArticleDO();
        newObj.setId(Long.parseLong(newId));
        List<ArticleDO> oldArticleList = articleDaoImpl.select(oldObj);
        List<ArticleDO> newArticleList = articleDaoImpl.select(newObj);
        if (oldArticleList != null && !oldArticleList.isEmpty()) {
            ArticleDO articleDO = oldArticleList.get(0);
            newObj.setOrders(articleDO.getOrders());
        }
        if (newArticleList != null && !newArticleList.isEmpty()) {
            ArticleDO articleDO = newArticleList.get(0);
            oldObj.setOrders(articleDO.getOrders());
        }
        articleDaoImpl.update(oldObj);
        articleDaoImpl.update(newObj);

    }

}
